#!/usr/bin/perl -w
use strict;
use File::Temp qw/ tempfile tempdir /;

# Load the different types of data structures
my %cgen_datatypes;
my @valid_mods = ();
my $server_code = "";
my $client_code = "";
sub load_code_gen_ds {
	open(CGEN, "codegen.data") or die "Unable to open codegen.data file\n";

	# Data Structure sizes section
	while (<CGEN>) {
		my $str = $_;
		chomp $str;
		next if ($str =~ /^#.*/);
		next if ($str =~ /^\s*$/);
		last if ($str =~ /^===/);

		my @cgarr = split(':', $str);
		$cgen_datatypes{$cgarr[0]} = $cgarr[1];
		print "sizeof($cgarr[0])=".$cgen_datatypes{$cgarr[0]}."\n";
	}

	# Data Structure modifiers section
	while (<CGEN>) {
		my $str = $_;
		chomp $str;
		next if ($str =~ /^#.*$/);
		next if ($str =~ /^\s*$/);
		last if ($str =~ /^===/);

		push(@valid_mods, $str);
	}

	# Server Code section
	while (<CGEN>) {
		my $str = $_;
		chomp $str;
		next if ($str =~ /^#.*/);
		next if ($str =~ /^\s*$/);
		last if ($str =~ /^===/);

		$server_code = $server_code.$str."\n";
	}

	# Client Code section
	while (<CGEN>) {
		my $str = $_;
		chomp $str;
		next if ($str =~ /^#.*/);
		next if ($str =~ /^\s*$/);
		last if ($str =~ /^===/);

		$client_code = $client_code.$str."\n";
	}
	print $server_code;
	print $client_code;
}
load_code_gen_ds;

if (not $ARGV[4]) {
	print "Usage: ./network_code_gen.pl input_file.nob serverout.h serverout.cpp clientout.h clientout.cpp\n";
	exit(-1);
}
my $inpf = $ARGV[0];
my $srvfh = $ARGV[1];
my $srvfc = $ARGV[2];
my $clifh = $ARGV[3];
my $clifc = $ARGV[4];

# Take Backups
system("cp -fv $srvfh $srvfh.NGENBAK") and die;
system("cp -fv $srvfc $srvfc.NGENBAK") and die;
system("cp -fv $clifh $clifh.NGENBAK") and die;
system("cp -fv $clifc $clifc.NGENBAK") and die;

my $tmpf = "/tmp/nggen.XXXXXX";

open(INP, $inpf) or die "Unable to open $inpf\n";

my $name = "";

# Replaces the % characters with actual variables
sub replace_comment
{
	my $str = $_[0];
	$str =~ s/\%P/$name/g;
	$str;
}

my @vartype = ();
my @varsize = ();
my @varname = ();
my @arrnum = ();
my @comment = ();
my $srvclassname;
my $cliclassname;
my $cnt = 0;
while (<INP>) {
	chomp;
	my $str = $_;
	next if ($str =~ /^#.*/);
	next if ($str =~ /^\s*$/);

	my @arr = split(':', $str);
	if ($arr[0] eq "name") {
		$name = $arr[1];
		print "Name = $name\n";
		next;
	}
	if ($arr[0] eq "srvclassname") {
		$srvclassname = $arr[1];
		print "SSName = $srvclassname\n";
		next;
	}
	
	if ($arr[0] eq "cliclassname") {
		$cliclassname= $arr[1];
		print "CCName = $cliclassname\n";
		next;
	}
	
	my $vartype = $arr[0];
	my $isarr = 0;
	if ($vartype =~ /\[\]/) {
		$vartype =~ s/\[\]//g;
		$isarr = 1;
	}

	my $lkvartype = $vartype;
	foreach my $mod (@valid_mods) {
		$lkvartype =~ s/$mod\s*//g;
	}
	my $sz = $cgen_datatypes{$lkvartype};
	print "Remaining($vartype) = $lkvartype  SZ=$sz\n";
	push(@vartype, $vartype);
	push(@varsize, $sz);
	push(@varname, $arr[1]);
	my $comment;
	if ($isarr) {
		push(@arrnum, $arr[2]);
		$comment = replace_comment $arr[3];
	} else {
		push(@arrnum, -1);
		$comment = replace_comment $arr[2];
	}
	push(@comment, $comment);

	$cnt++;
	print "$vartype $varname[-1] $arrnum[-1]; /* $comment */\n";
}
close INP;


# Sub
sub gethstr {
	my $str = "";
	for (my $i = 0; $i < $cnt; $i++) {
		if ($arrnum[$i] == -1) {
			$str .= "\t\t$vartype[$i] $varname[$i]; /* $comment[$i] */\n";
		} else {
			$str .= "\t\t$vartype[$i] $varname[$i]\[$arrnum[$i]\]; /* $comment[$i] */\n";
		}
	}
	$str;
}

# Server.h
system("cp -f $srvfh $tmpf") and die;
open(TMPF, "$tmpf") or die;
open(OUTF, ">$srvfh") or die;

my $skip = 0;
while (<TMPF>) {
	my $str = $_;
	if (/AUTOGENERATED CODE END/) {
		$skip = 0;
	}
	if (not $skip) {
		print OUTF $str;
	}
	if (/AUTOGENERATED CODE START/) {
		$skip = 1;
		print OUTF gethstr();
	}
}
close TMPF;
close OUTF;

# Client.h
system("cp -f $clifh $tmpf") and die;
open(TMPF, "$tmpf") or die;
open(OUTF, ">$clifh") or die;

$skip = 0;
while (<TMPF>) {
	my $str = $_;
	if (/AUTOGENERATED CODE END/) {
		$skip = 0;
	}
	if (not $skip) {
		print OUTF $str;
	}
	if (/AUTOGENERATED CODE START/) {
		$skip = 1;
		print OUTF gethstr();
	}
}
close TMPF;
close OUTF;

sub replace_cppcode
{
	my $str = $_[0];
	my $i = $_[1];
	my $isserv = $_[2];
	print
	$str =~ s/\%P/$name/g;
	if ($isserv == 1) {
		$str =~ s/\%C/$srvclassname/g;
	} else {
		$str =~ s/\%C/$cliclassname/g;
	}
	$str =~ s/\%N/$varname[$i]/g;
	$str =~ s/\%T/$vartype[$i]/g;
	$str =~ s/\%S/$varsize[$i]/g;
	$str =~ s/\%A/$arrnum[$i]/g;
	$str;
}

sub getcppcode
{
	my $basecode = $_[0];
	my $isserver = $_[1];
	my $str = "";
	my @arr = split('\n', $basecode);

	foreach my $line (@arr) {
		my @linesplit = split(':', $line);
		my $cppline = substr $line, 5;
		if ($linesplit[0] eq "NORM") {
			$str .= replace_cppcode($cppline, 0, 1)."\n";
		} elsif ($linesplit[0] eq "RNRM") {
			for (my $i = 0; $i < $cnt; $i++) {
				if ($arrnum[$i] == -1) {
					$str .= replace_cppcode($cppline, $i, $isserver)."\n";
				}
			}
		} elsif ($linesplit[0] eq "RARR") {
			for (my $i = 0; $i < $cnt; $i++) {
				if ($arrnum[$i] != -1) {
					$str .= replace_cppcode($cppline, $i, $isserver)."\n";
				}
			}
		}
	}
	$str;
}

# Server.cpp
system("cp -f $srvfc $tmpf") and die;
open(TMPF, "$tmpf") or die;
open(OUTF, ">$srvfc") or die;

$skip = 0;
while (<TMPF>) {
	my $str = $_;
	if (/AUTOGENERATED CODE END/) {
		$skip = 0;
	}
	if (not $skip) {
		print OUTF $str;
	}
	if (/AUTOGENERATED CODE START/) {
		$skip = 1;
		print OUTF getcppcode($server_code, 1);
	}
}
close TMPF;
close OUTF;

# Client.cpp
system("cp -f $clifc $tmpf") and die;
open(TMPF, "$tmpf") or die;
open(OUTF, ">$clifc") or die;

$skip = 0;
while (<TMPF>) {
	my $str = $_;
	if (/AUTOGENERATED CODE END/) {
		$skip = 0;
	}
	if (not $skip) {
		print OUTF $str;
	}
	if (/AUTOGENERATED CODE START/) {
		$skip = 1;
		print OUTF getcppcode($client_code, 0);
	}
}
close TMPF;
close OUTF;



